// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use serde_json::{json, Value as Json};
use std::borrow::Cow;
use tw_encoding::hex::{DecodeHex, ToHex};
use tw_evm::abi::AbiErrorKind;
use tw_evm::evm_context::StandardEvmContext;
use tw_evm::modules::abi_encoder::AbiEncoder;
use tw_number::{I256, U256};
use tw_proto::EthereumAbi::Proto;

use Proto::mod_ParamType::OneOfparam as ParamTypeEnum;
use Proto::mod_ParamsDecodingInput::OneOfabi as AbiEnum;
use Proto::mod_Token::OneOftoken as TokenEnum;

const SWAP_V2_ABI: &str = include_str!("data/swap_v2.json");
const SWAP_V2_DECODED: &str = include_str!("data/swap_v2_decoded.json");

fn param(name: &str, kind: ParamTypeEnum<'static>) -> Proto::Param<'static> {
    Proto::Param {
        name: name.to_string().into(),
        param: Some(Proto::ParamType { param: kind }),
    }
}

fn named_token(name: &str, token: TokenEnum<'static>) -> Proto::Token<'static> {
    Proto::Token {
        name: Cow::Owned(name.to_string()),
        token,
    }
}

fn u_number_n<const BITS: u32>(value: u64) -> TokenEnum<'static> {
    TokenEnum::number_uint(Proto::NumberNParam {
        bits: BITS,
        value: U256::encode_be_compact(value),
    })
}

fn s_number_n<const BITS: u32>(value: i64) -> TokenEnum<'static> {
    TokenEnum::number_int(Proto::NumberNParam {
        bits: BITS,
        value: I256::encode_be_compact(value),
    })
}

fn number_type_n<const BITS: u32>() -> Proto::NumberNType {
    Proto::NumberNType { bits: BITS }
}

fn array(
    element_type: ParamTypeEnum<'static>,
    values: Vec<TokenEnum<'static>>,
) -> Proto::ArrayParam<'static> {
    let elements = values
        .into_iter()
        .map(|token| Proto::Token {
            name: "".into(),
            token,
        })
        .collect();
    let element_type = Some(Proto::ParamType {
        param: element_type,
    });
    Proto::ArrayParam {
        element_type,
        elements,
    }
}

fn array_type(element_type: ParamTypeEnum<'static>) -> Box<Proto::ArrayType<'static>> {
    Box::new(Proto::ArrayType {
        element_type: Some(Box::new(Proto::ParamType {
            param: element_type,
        })),
    })
}

fn tuple<I>(params: I) -> TokenEnum<'static>
where
    I: IntoIterator<Item = Proto::Token<'static>>,
{
    TokenEnum::tuple(Proto::TupleParam {
        params: params.into_iter().collect(),
    })
}

fn test_encode_contract_call_impl(
    function_name: &str,
    tokens: Vec<Proto::Token<'_>>,
    expected_call: &str,
) {
    let input = Proto::FunctionEncodingInput {
        function_name: function_name.into(),
        tokens,
    };

    let output = AbiEncoder::<StandardEvmContext>::encode_contract_call(input);
    assert_eq!(output.error, AbiErrorKind::OK);
    assert!(output.error_message.is_empty());
    assert_eq!(output.encoded.to_hex(), expected_call);
}

fn test_decode_params_impl(
    encoded: &str,
    expected_tokens: Vec<Proto::Token<'_>>,
    abi: AbiEnum<'_>,
) {
    let input = Proto::ParamsDecodingInput {
        encoded: encoded.decode_hex().unwrap().into(),
        abi,
    };

    let output = AbiEncoder::<StandardEvmContext>::decode_params(input);
    assert_eq!(output.error, AbiErrorKind::OK);
    assert!(output.error_message.is_empty());
    assert_eq!(output.tokens, expected_tokens);
}

fn test_decode_contract_call_impl(
    encoded: &str,
    abi_json: &str,
    decoded_json: &str,
    expected_tokens: Vec<Proto::Token<'_>>,
) {
    let input = Proto::ContractCallDecodingInput {
        encoded: encoded.decode_hex().unwrap().into(),
        smart_contract_abi_json: Cow::Borrowed(abi_json),
    };

    let output = AbiEncoder::<StandardEvmContext>::decode_contract_call(input);
    assert_eq!(output.error, AbiErrorKind::OK);
    assert!(output.error_message.is_empty());

    let actual_json: Json = serde_json::from_str(&output.decoded_json).unwrap();
    let expected_json: Json = serde_json::from_str(decoded_json).unwrap();
    assert_eq!(actual_json, expected_json);
    assert_eq!(output.tokens, expected_tokens);
}

mod swap_v2 {
    use super::*;

    const FUNCTION_NAME: &str = "callBridgeCall";
    const ENCODED_CALL: &str = "846a1bc6000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000820000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000003840651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d66600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";

    fn calls_array_type() -> ParamTypeEnum<'static> {
        ParamTypeEnum::tuple(Proto::TupleType {
            params: vec![
                param("callType", ParamTypeEnum::number_uint(number_type_n::<8>())),
                param("target", ParamTypeEnum::address(Proto::AddressType {})),
                param("value", ParamTypeEnum::number_uint(number_type_n::<256>())),
                param(
                    "callData",
                    ParamTypeEnum::byte_array(Proto::ByteArrayType {}),
                ),
                param(
                    "payload",
                    ParamTypeEnum::byte_array(Proto::ByteArrayType {}),
                ),
            ],
        })
    }

    fn token_values() -> Vec<Proto::Token<'static>> {
        let call_data_1 = "0x095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000".decode_hex().unwrap();
        let call_data_2 = "0x0651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666".decode_hex().unwrap();

        let calls = vec![
            tuple(vec![
                named_token("callType", u_number_n::<8>(0)),
                named_token(
                    "target",
                    TokenEnum::address("0xdAC17F958D2ee523a2206206994597C13D831ec7".into()),
                ),
                named_token("value", u_number_n::<256>(0)),
                named_token("callData", TokenEnum::byte_array(call_data_1.into())),
                named_token("payload", TokenEnum::byte_array(Cow::default())),
            ]),
            tuple(vec![
                named_token("callType", u_number_n::<8>(0)),
                named_token(
                    "target",
                    TokenEnum::address("0x99a58482BD75cbab83b27EC03CA68fF489b5788f".into()),
                ),
                named_token("value", u_number_n::<256>(0)),
                named_token("callData", TokenEnum::byte_array(call_data_2.into())),
                named_token("payload", TokenEnum::byte_array(Cow::default())),
            ]),
        ];

        let payload = "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".decode_hex().unwrap();
        vec![
            named_token(
                "token",
                TokenEnum::address("0xdAC17F958D2ee523a2206206994597C13D831ec7".into()),
            ),
            named_token("amount", u_number_n::<256>(20000000000000000)),
            named_token("calls", TokenEnum::array(array(calls_array_type(), calls))),
            named_token("bridgedTokenSymbol", TokenEnum::string_value("USDC".into())),
            named_token(
                "destinationChain",
                TokenEnum::string_value("binance".into()),
            ),
            named_token(
                "destinationAddress",
                TokenEnum::string_value("0xce16F69375520ab01377ce7B88f5BA8C48F8D666".into()),
            ),
            named_token("payload", TokenEnum::byte_array(payload.into())),
            named_token(
                "gasRefundRecipient",
                TokenEnum::address("0xa140F413C63FBDA84E9008607E678258ffFbC76b".into()),
            ),
            named_token("enableExpress", TokenEnum::boolean(true)),
        ]
    }

    #[test]
    fn test_decode_contract_call_swap_v2() {
        test_decode_contract_call_impl(ENCODED_CALL, SWAP_V2_ABI, SWAP_V2_DECODED, token_values());
    }

    #[test]
    fn test_encode_contract_call_swap_v2() {
        test_encode_contract_call_impl(FUNCTION_NAME, token_values(), ENCODED_CALL);
    }
}

#[test]
fn test_decode_params_with_abi_json() {
    let abi_json = json!([
        {
            "internalType": "bytes32",
            "name": "node",
            "type": "bytes32"
        },
        {
            "internalType": "address",
            "name": "resolver",
            "type": "address"
        }
    ]);
    let abi_json = serde_json::to_string(&abi_json).unwrap();
    let encoded = "e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41";

    let node_bytes = "0xe71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f"
        .decode_hex()
        .unwrap();
    let expected_tokens = vec![
        named_token("node", TokenEnum::byte_array_fix(node_bytes.into())),
        named_token(
            "resolver",
            TokenEnum::address("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41".into()),
        ),
    ];

    test_decode_params_impl(encoded, expected_tokens, AbiEnum::abi_json(abi_json.into()));
}

#[test]
fn test_decode_params_with_abi_params() {
    let encoded = "e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41";

    let node_bytes = "0xe71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f"
        .decode_hex()
        .unwrap();
    let expected_tokens = vec![
        named_token("node", TokenEnum::byte_array_fix(node_bytes.into())),
        named_token(
            "resolver",
            TokenEnum::address("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41".into()),
        ),
    ];

    let abi_params = vec![
        param(
            "node",
            ParamTypeEnum::byte_array_fix(Proto::ByteArrayFixType { size: 32 }),
        ),
        param("resolver", ParamTypeEnum::address(Proto::AddressType {})),
    ];

    test_decode_params_impl(
        encoded,
        expected_tokens,
        AbiEnum::abi_params(Proto::AbiParams { params: abi_params }),
    );
}

mod dynamic_arguments {
    use super::*;

    const FUNCTION_NAME: &str = "f";
    const ENCODED_CALL_WITH_SIGNATURE: &str = "47b941bf00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000";
    const ENCODED_CALL: &str = "00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000";

    fn token_values() -> Vec<Proto::Token<'static>> {
        let dynamic_array = array(
            ParamTypeEnum::number_uint(number_type_n::<32>()),
            vec![u_number_n::<32>(0x456), u_number_n::<32>(0x789)],
        );
        let byte_array = vec![0x31u8, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30];
        vec![
            named_token("", u_number_n::<256>(0x123)),
            named_token("", TokenEnum::array(dynamic_array)),
            named_token("", TokenEnum::byte_array_fix(byte_array.into())),
            named_token("", TokenEnum::string_value("Hello, world!".into())),
        ]
    }

    fn param_types() -> Vec<Proto::Param<'static>> {
        vec![
            param("", ParamTypeEnum::number_uint(number_type_n::<256>())),
            param(
                "",
                ParamTypeEnum::array(array_type(
                    ParamTypeEnum::number_uint(number_type_n::<32>()),
                )),
            ),
            param(
                "",
                ParamTypeEnum::byte_array_fix(Proto::ByteArrayFixType { size: 10 }),
            ),
            param("", ParamTypeEnum::string_param(Proto::StringType {})),
        ]
    }

    #[test]
    fn test_encode_contract_call_with_dynamic_arguments() {
        test_encode_contract_call_impl(FUNCTION_NAME, token_values(), ENCODED_CALL_WITH_SIGNATURE);
    }

    #[test]
    fn test_decode_params_with_dynamic_arguments_case2() {
        test_decode_params_impl(
            ENCODED_CALL,
            token_values(),
            AbiEnum::abi_params(Proto::AbiParams {
                params: param_types(),
            }),
        );
    }
}

mod negative_integers {
    use super::*;

    const FUNCTION_NAME: &str = "approve";
    const ENCODED_CALL_WITH_SIGNATURE: &str = "d4e12f2e0000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaedfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd";
    const ENCODED_CALL: &str = "0000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaedfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd";

    const ABI_JSON: &str = include_str!("data/abi_with_negative_int.json");
    const DECODED_JSON: &str = include_str!("data/decoded_abi_with_negative_int.json");

    fn token_values() -> Vec<Proto::Token<'static>> {
        vec![
            named_token(
                "_spender",
                TokenEnum::address("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed".into()),
            ),
            named_token("_value", s_number_n::<256>(-3)),
        ]
    }

    fn param_types() -> Vec<Proto::Param<'static>> {
        vec![
            param("_spender", ParamTypeEnum::address(Proto::AddressType {})),
            param("_value", ParamTypeEnum::number_int(number_type_n::<256>())),
        ]
    }

    #[test]
    fn test_encode_contract_call_with_negative_integer() {
        test_encode_contract_call_impl(FUNCTION_NAME, token_values(), ENCODED_CALL_WITH_SIGNATURE);
    }

    #[test]
    fn test_decode_params_with_negative_integer() {
        test_decode_params_impl(
            ENCODED_CALL,
            token_values(),
            AbiEnum::abi_params(Proto::AbiParams {
                params: param_types(),
            }),
        );
    }

    #[test]
    fn test_decode_contract_call_with_negative_integer() {
        test_decode_contract_call_impl(
            ENCODED_CALL_WITH_SIGNATURE,
            ABI_JSON,
            DECODED_JSON,
            token_values(),
        );
    }
}

#[test]
fn test_encode_params_monster() {
    let byte_array = "3132333435".decode_hex().unwrap();

    let u1 = u_number_n::<8>(1);
    let u2 = u_number_n::<16>(2);
    let u3 = u_number_n::<32>(3);
    let u4 = u_number_n::<64>(4);
    let u5 = u_number_n::<168>(0x123);
    let u6 = u_number_n::<256>(0x123);

    let u1t = ParamTypeEnum::number_uint(number_type_n::<8>());
    let u2t = ParamTypeEnum::number_uint(number_type_n::<16>());
    let u3t = ParamTypeEnum::number_uint(number_type_n::<32>());
    let u4t = ParamTypeEnum::number_uint(number_type_n::<64>());
    let u5t = ParamTypeEnum::number_uint(number_type_n::<168>());
    let u6t = ParamTypeEnum::number_uint(number_type_n::<256>());

    let i1 = s_number_n::<8>(1);
    let i2 = s_number_n::<16>(2);
    let i3 = s_number_n::<32>(3);
    let i4 = s_number_n::<64>(4);
    let i5 = s_number_n::<168>(0x123);
    let i6 = s_number_n::<256>(0x123);

    let i1t = ParamTypeEnum::number_int(number_type_n::<8>());
    let i2t = ParamTypeEnum::number_int(number_type_n::<16>());
    let i3t = ParamTypeEnum::number_int(number_type_n::<32>());
    let i4t = ParamTypeEnum::number_int(number_type_n::<64>());
    let i5t = ParamTypeEnum::number_int(number_type_n::<168>());
    let i6t = ParamTypeEnum::number_int(number_type_n::<256>());

    let b = TokenEnum::boolean(true);
    let bt = ParamTypeEnum::boolean(Proto::BoolType {});
    let s = TokenEnum::string_value("Hello, world!".into());
    let st = ParamTypeEnum::string_param(Proto::StringType {});
    let a = TokenEnum::address("0xf784682c82526e245f50975190ef0fff4e4fc077".into());
    let at = ParamTypeEnum::address(Proto::AddressType {});
    let bytes = TokenEnum::byte_array(byte_array.clone().into());
    let bytes_t = ParamTypeEnum::byte_array(Proto::ByteArrayType {});
    let fbytes = TokenEnum::byte_array_fix(byte_array.clone().into());
    let fbytes_t = ParamTypeEnum::byte_array_fix(Proto::ByteArrayFixType {
        size: byte_array.len() as u64,
    });

    let tokens = vec![
        // Uint
        named_token("u1", u1.clone()),
        named_token("u2", u2.clone()),
        named_token("u3", u3.clone()),
        named_token("u4", u4.clone()),
        named_token("u5", u5.clone()),
        named_token("u6", u6.clone()),
        // Int
        named_token("i1", i1.clone()),
        named_token("i2", i2.clone()),
        named_token("i3", i3.clone()),
        named_token("i4", i4.clone()),
        named_token("i5", i5.clone()),
        named_token("i6", i6.clone()),
        // Single params
        named_token("b", b.clone()),
        named_token("s", s.clone()),
        named_token("a", a.clone()),
        named_token("bytes", bytes.clone()),
        named_token("fbytes", fbytes.clone()),
        // Array<Uint>
        named_token("a_u1", TokenEnum::array(array(u1t, vec![u1]))),
        named_token("a_u2", TokenEnum::array(array(u2t, vec![u2]))),
        named_token("a_u3", TokenEnum::array(array(u3t, vec![u3]))),
        named_token("a_u4", TokenEnum::array(array(u4t, vec![u4]))),
        named_token("a_u5", TokenEnum::array(array(u5t, vec![u5]))),
        named_token("a_u6", TokenEnum::array(array(u6t, vec![u6]))),
        // Array<Int>
        named_token("a_i1", TokenEnum::array(array(i1t, vec![i1]))),
        named_token("a_i2", TokenEnum::array(array(i2t, vec![i2]))),
        named_token("a_i3", TokenEnum::array(array(i3t, vec![i3]))),
        named_token("a_i4", TokenEnum::array(array(i4t, vec![i4]))),
        named_token("a_i5", TokenEnum::array(array(i5t, vec![i5]))),
        named_token("a_i6", TokenEnum::array(array(i6t, vec![i6]))),
        // Arrays with single params
        named_token("a_b", TokenEnum::array(array(bt, vec![b]))),
        named_token("a_s", TokenEnum::array(array(st, vec![s]))),
        named_token("a_a", TokenEnum::array(array(at, vec![a]))),
        named_token("a_bytes", TokenEnum::array(array(bytes_t, vec![bytes]))),
        named_token("a_fbytes", TokenEnum::array(array(fbytes_t, vec![fbytes]))),
    ];
    let encoding_input = Proto::FunctionEncodingInput {
        function_name: "monster".into(),
        tokens,
    };
    let output = AbiEncoder::<StandardEvmContext>::encode_contract_call(encoding_input);

    assert_eq!(output.error, AbiErrorKind::OK);
    assert!(output.error_message.is_empty());

    // let expected_encoded = "4061f075000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000030031323334350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000531323334350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013132333435000000000000000000000000000000000000000000000000000000";
    let expected_encoded = "70efb5a500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000440000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000000000000000000000000000000000000000000480313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000068000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000008c00000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000531323334350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013132333435000000000000000000000000000000000000000000000000000000";
    assert_eq!(output.encoded.to_hex(), expected_encoded);
}

#[test]
fn test_decode_value() {
    struct TestInput {
        encoded: &'static str,
        kind: &'static str,
        expected_value_str: &'static str,
        expected_value_proto: TokenEnum<'static>,
    }

    let num1 = u_number_n::<8>(49);
    let num2 = u_number_n::<8>(50);
    let num3 = u_number_n::<8>(51);
    let address1 = TokenEnum::address("0xF784682C82526e245F50975190EF0fff4E4fC077".into());
    let address2 = TokenEnum::address("0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3".into());
    let bytes1 = TokenEnum::byte_array("0x1011".decode_hex().unwrap().into());
    let bytes2 = TokenEnum::byte_array("0x102222".decode_hex().unwrap().into());

    let test_values = [
        TestInput {
            encoded: "0000000000000000000000000000000000000000000000000000091d0eb3e2af",
            kind: "uint256",
            expected_value_str: "10020405371567",
            expected_value_proto: u_number_n::<256>(10020405371567),
        },
        TestInput {
            encoded: "0000000000000000000000000000000000000000000000000000091d0eb3e2af0000000000000000000000000000000000000000000000000000000000000000",
            kind: "int256",
            expected_value_str: "10020405371567",
            expected_value_proto: s_number_n::<256>(10020405371567),
        },
        TestInput {
            encoded: "000000000000000000000000000000000000000000000000000000000000002a",
            kind: "uint",
            expected_value_str: "42",
            expected_value_proto: u_number_n::<256>(42),
        },
        TestInput {
            encoded: "0000000000000000000000000000000000000000000000000000000000000018",
            kind: "uint8",
            expected_value_str: "24",
            expected_value_proto: u_number_n::<8>(24),
        },
        TestInput {
            encoded: "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033",
            kind: "uint8[]",
            expected_value_str: "[49,50,51]",
            expected_value_proto: TokenEnum::array(array(ParamTypeEnum::number_uint(number_type_n::<8>()), vec![num1, num2, num3])),
        },
        TestInput {
            encoded: "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077",
            kind: "address",
            expected_value_str: "0xF784682C82526e245F50975190EF0fff4E4fC077",
            expected_value_proto: TokenEnum::address(
                "0xF784682C82526e245F50975190EF0fff4E4fC077".into(),
            ),
        },
        TestInput {
            encoded: "000000000000000000000000000000000000000000000000000000000000002c48656c6c6f20576f726c64212020202048656c6c6f20576f726c64212020202048656c6c6f20576f726c64210000000000000000000000000000000000000000",
            kind: "string",
            expected_value_str: "Hello World!    Hello World!    Hello World!",
            expected_value_proto: TokenEnum::string_value(
                "Hello World!    Hello World!    Hello World!".into(),
            ),
        },
        TestInput {
            encoded: "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3",
            kind: "address[]",
            expected_value_str: r#"["0xF784682C82526e245F50975190EF0fff4E4fC077","0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3"]"#,
            expected_value_proto: TokenEnum::array(
                array(ParamTypeEnum::address(Proto::AddressType {}), vec![address1, address2])),
        },
        TestInput {
            encoded: "3132333435363738393000000000000000000000000000000000000000000000",
            kind: "bytes10",
            expected_value_str: "0x31323334353637383930",
            expected_value_proto: TokenEnum::byte_array_fix(
                "0x31323334353637383930".decode_hex().unwrap().into(),
            ),
        },
        TestInput {
            encoded: "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002101100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031022220000000000000000000000000000000000000000000000000000000000",
            kind: "bytes[]",
            expected_value_str: r#"["0x1011","0x102222"]"#,
            expected_value_proto: TokenEnum::array(
                array(ParamTypeEnum::byte_array(Proto::ByteArrayType {}), vec![bytes1, bytes2])
            ),
        },
    ];

    for test in test_values {
        let input = Proto::ValueDecodingInput {
            encoded: test.encoded.decode_hex().unwrap().into(),
            param_type: test.kind.into(),
        };
        let output = AbiEncoder::<StandardEvmContext>::decode_value(input);
        assert_eq!(output.error, AbiErrorKind::OK);
        assert!(output.error_message.is_empty());

        assert_eq!(output.token.unwrap().token, test.expected_value_proto);
        assert_eq!(output.param_str, test.expected_value_str);
    }
}

#[test]
fn test_decode_value_error() {
    #[track_caller]
    fn test_decode_value_error_impl(kind: &str, encoded: &str) {
        let input = Proto::ValueDecodingInput {
            encoded: encoded.decode_hex().unwrap().into(),
            param_type: kind.into(),
        };
        let output = AbiEncoder::<StandardEvmContext>::decode_value(input);
        assert_ne!(output.error, AbiErrorKind::OK);
        assert!(!output.error_message.is_empty());
    }

    test_decode_value_error_impl("tuple[7][][2][2]", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000375696e742a6c");
    test_decode_value_error_impl("bytes0[0][][2][6]", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000375696e742a6c");
}

#[test]
fn test_encode_params_invalid_address() {
    struct TestInput {
        token: TokenEnum<'static>,
        error: AbiErrorKind,
    }

    let test_inputs = [
        // No address '0x' prefix.
        TestInput {
            token: TokenEnum::address("f784682c82526e245f50975190ef0fff4e4fc077".into()),
            error: AbiErrorKind::Error_invalid_address_value,
        },
        // There is no uint0.
        TestInput {
            token: u_number_n::<0>(0),
            error: AbiErrorKind::Error_invalid_uint_value,
        },
        // There is no int7.
        TestInput {
            token: s_number_n::<7>(1),
            error: AbiErrorKind::Error_invalid_uint_value,
        },
        // There is no uint512.
        TestInput {
            token: u_number_n::<512>(123),
            error: AbiErrorKind::Error_invalid_uint_value,
        },
        // ArrayType::element_type and token mismatch.
        TestInput {
            token: TokenEnum::array(array(
                ParamTypeEnum::number_uint(number_type_n::<8>()),
                vec![u_number_n::<256>(1000)],
            )),
            error: AbiErrorKind::Error_invalid_param_type,
        },
    ];

    for TestInput { token, error } in test_inputs {
        let encoding_input = Proto::FunctionEncodingInput {
            function_name: "NonExisting".into(),
            tokens: vec![named_token("", token.clone())],
        };

        let output = AbiEncoder::<StandardEvmContext>::encode_contract_call(encoding_input);
        assert_eq!(
            output.error, error,
            "Expected error on encoding {:?}",
            token
        );
        assert!(!output.error_message.is_empty());
    }
}

#[test]
fn test_decode_contract_call_error() {
    let encoded_input = "11223344000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000820000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000003840651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d66600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".decode_hex().unwrap();
    let input = Proto::ContractCallDecodingInput {
        encoded: Cow::Owned(encoded_input),
        smart_contract_abi_json: Cow::Borrowed(SWAP_V2_ABI),
    };

    let output = AbiEncoder::<StandardEvmContext>::decode_contract_call(input);
    assert_eq!(output.error, AbiErrorKind::Error_abi_mismatch);
    assert!(!output.error_message.is_empty());
}
